#
# 【调度器】
#
# 调度器又称为引擎，是爬虫逻辑实现的模块。
#
# 爬虫逻辑可分解为如下几个部分：
#
# 1. 查询管理器中是否有待爬取的 URL
# 2. 调度器从管理器中获取一个待爬取 URL
# 3. 将获取到的 URL 交给下载器处理
# 4. 将下载器获取到的网页数据交给解析器处理
# 5. 将解析器处理后的 URL 集合交给管理器
# 6. 将解析器处理后的数据交给输出器
# 7. 重复上述步骤
#
from abc import abstractmethod
from typing import Union

from requests.cookies import RequestsCookieJar

from sam.util.crawler.lite.download import Download
from sam.util.crawler.lite.manager import Manager
from sam.util.crawler.lite.output import Output
from sam.util.crawler.lite.parser import Parser
from sam.util.httpUtil import get_url_encode_str
from sam.util.logUtil import LoggerUtil


class BaseSpider(LoggerUtil, Download, Parser, Output, Manager):
    def __init__(self, name: str = "myCrawl", level: str = "debug"):
        super().__init__(name=name, level=level)

    @abstractmethod
    def prepare_cookies(self) -> Union[dict, RequestsCookieJar]:
        pass

    @abstractmethod
    def prepare_proxies(self) -> dict:
        pass

    @abstractmethod
    def parse_url(self, url, soup) -> list:
        pass

    @abstractmethod
    def parse_data(self, url, soup) -> list:
        pass

    def start(self, url: str):
        # 爬虫计数器
        count = 0
        # 将第一个 URL 放入管理器中
        self.add_url(url)
        # 判断管理器中 URL 的数量是否为 0
        while self.num_url():
            try:
                # 从管理器中获取一个 URL
                new_url = self.get_url()
                self.logger.info(f"当前的 url是: {new_url}")
                # 将获取到的 URL 交给下载器处理
                html_cont = self.download(new_url)
                # self.logger.info(f"下载的html 是: {html_cont}")
                # 将 URL 和 下载器处理后的网页数据交给解析器处理
                new_urls, new_data = self.parser(new_url, html_cont)
                self.logger.info(f"解析后的结果 是: {new_url}, {new_data}")
                # 将解析器处理后的数据和新的 URL 分别交给管理器和输出器
                self.add_urls(new_urls)
                self.logger.info(f"添加了 新的连接")
                self.add_data(new_data)
                self.logger.info(f"添加了 新的数据集")
                count += 1
                self.logger.info(f"计数器加一： {count}")
                self.logger.info(f"{self.old_urls} \n {self.new_urls}")
                self.logger.info('爬虫运行状态 ==> %d : %s' % (count, new_url))
            except Exception as e:
                self.logger.info(f'爬虫遇到错误, {e}')
        else:
            # 当爬虫爬取完所有数据后，输出器将数据输出到文件
            self.output()

    def query_once(self, url: str, optional: str = "bySearch") -> list:

        # 将获取到的 URL 交给下载器处理
        html_cont = self.download(url)
        if len(html_cont) <= 250:
            return []
        list_page_urls, new_data_null = self.parser(url, html_cont)
        # self.logger.info(f"解析后的结果 是: {list_page_urls}, {new_data_null}")
        if optional == "bySearch":
            if list_page_urls:
                html_cont = self.download(list_page_urls[0])
                # self.logger.info(f"下载的html 是: {html_cont}")
                # 将 URL 和 下载器处理后的网页数据交给解析器处理
                next_urls_null, detail_page_data = self.parser(list_page_urls[0], html_cont)
                # self.logger.info(f"解析后的结果 是: {next_urls_null}, {detail_page_data}")
                if detail_page_data:
                    return [detail_page_data]
        else:
            return [new_data_null]

    def query_list(self, url: str, optional: str = "bySearch") -> list:

        # 将获取到的 URL 交给下载器处理
        html_cont = self.download(url)
        if len(html_cont) <= 250:
            return []
        list_page_urls, new_data_null = self.parser(url, html_cont)
        return [list_page_urls]


class CombineSpider(LoggerUtil):
    def __init__(self
                 , parser: Parser
                 , download: Download = Download()
                 , name: str = "myCrawl"
                 , output: Output = Output()
                 ):
        # 实例化 管理器、下载器、解析器、输出器
        super().__init__(name=name, level="debug")
        self.download = download
        self.parser = parser
        self.output = output
        self.manager = Manager()

    def spider(self, url: str, max_count: int = None):
        # 爬虫计数器
        count = 0
        # 将第一个 URL 放入管理器中
        self.manager.add_url(url)
        # 判断管理器中 URL 的数量是否为 0
        while self.manager.num_url():
            try:
                # 从管理器中获取一个 URL
                new_url = self.manager.get_url()
                # 将获取到的 URL 交给下载器处理
                html_cont = self.download.download(new_url)
                # 将 URL 和 下载器处理后的网页数据交给解析器处理
                new_manager, new_data = self.parser.parser(new_url, html_cont)
                # 将解析器处理后的数据和新的 URL 分别交给管理器和输出器
                self.manager.add_urls(new_manager)
                self.output.add_data(new_data)
                count += 1
                self.logger.info('爬虫运行状态 ==> %d : %s' % (count, new_url))
                # 最多爬取 100 次
                if max_count and count > 10:
                    break
            except Exception as e:
                self.logger.info(f'爬虫遇到错误, {e}')
            else:
                # 当爬虫爬取完所有数据后，输出器将数据输出到文件
                self.output.output()


if __name__ == '__main__':
    # URL 入口（爬虫爬取的第一个 URL）
    client_name_encode = get_url_encode_str("杭州佳偲贸易有限公司中山中路分公司")
    begin_url = f'https://www.qichacha.com/search?key={client_name_encode}'
    # 实例化调度器并启动爬虫
    run = CombineSpider()
    run.spider(begin_url)
